package org.finesys.common.security.core.support;

import cn.hutool.extra.spring.SpringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.finesys.common.constants.SecurityConstants;
import org.finesys.common.security.core.module.AuthUser;
import org.finesys.common.security.core.service.AuthUserService;
import org.springframework.core.Ordered;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;

import java.security.Principal;
import java.util.Comparator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * 不透明token加强
 */
@Slf4j
@RequiredArgsConstructor
public class AuthOpaqueTokenIntrospector implements OpaqueTokenIntrospector {

    private final OAuth2AuthorizationService oAuth2AuthorizationService;


    @Override
    public OAuth2AuthenticatedPrincipal introspect(String token) {
        //获取验证用户信息
        OAuth2Authorization authorization = oAuth2AuthorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
        if (Objects.isNull(authorization)) {
            throw new InvalidBearerTokenException(token);
        }
        //客户端模式验证
        if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(authorization.getAuthorizationGrantType())) {
            return new DefaultOAuth2AuthenticatedPrincipal(authorization.getPrincipalName(), authorization.getAttributes(), AuthorityUtils.NO_AUTHORITIES);
        }
        Map<String, AuthUserService> userServiceMap = SpringUtil.getBeansOfType(AuthUserService.class);

        Optional<AuthUserService> optional = userServiceMap.values().stream()
                .filter(authUserService -> authUserService.support(Objects.requireNonNull(authorization).getRegisteredClientId(),
                        authorization.getAuthorizationGrantType().getValue())).max(Comparator.comparingInt(Ordered::getOrder));
        //获取用户
        UserDetails userDetails = null;

        try {
            Object principal = Objects.requireNonNull(authorization).getAttributes().get(Principal.class.getName());
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal;
            Object tokenPrincipal = usernamePasswordAuthenticationToken.getPrincipal();
            userDetails = optional.get().loadUserByAuthUser((AuthUser) tokenPrincipal);
        } catch (UsernameNotFoundException notFoundException) {
            log.warn("用户不不存在 {}", notFoundException.getLocalizedMessage());
            throw notFoundException;
        } catch (Exception e) {
            log.error("资源服务器 introspect Token error {}", e.getLocalizedMessage());
        }
        // 注入扩展属性,方便上下文获取客户端ID
        AuthUser user = (AuthUser) userDetails;
        Objects.requireNonNull(user).getAttributes().put(SecurityConstants.CLIENT_ID, authorization.getRegisteredClientId());
        return user;
    }
}
